var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
define(["require", "exports", "path", "vscode-languageclient", "./htmlEmptyTagsShared", "./tagClosing", "vscode-languageclient", "vscode-languageserver-protocol", "vscode-languageserver-protocol", "./modes/languageModes", "./modes/formatting", "./utils/arrays", "vscode-languageserver-types", "./utils/documentContext"], function (require, exports, path, vscode_languageclient_1, htmlEmptyTagsShared_1, tagClosing_1, vsclient, vsproto, vscode_languageserver_protocol_1, languageModes_1, formatting_1, arrays_1, vscode_languageserver_types_1, documentContext_1) {
    'use strict';
    Object.defineProperty(exports, "__esModule", { value: true });
    var TagCloseRequest;
    (function (TagCloseRequest) {
        TagCloseRequest.type = new vscode_languageserver_protocol_1.RequestType('html/tag');
    })(TagCloseRequest || (TagCloseRequest = {}));
    function formatError(message, err) {
        if (err instanceof Error) {
            let error = err;
            return `${message}: ${error.message}\n${error.stack}`;
        }
        else if (typeof err === 'string') {
            return `${message}: ${err}`;
        }
        else if (err) {
            return `${message}: ${err.toString()}`;
        }
        return message;
    }
    function runSafe(func, errorVal, errorMessage, detail) {
        try {
            let t = func();
            if (t instanceof Promise) {
                return t.then((v) => {
                    detail.resolver(v);
                    return v;
                }, e => {
                    detail.rejecter(formatError(errorMessage, e));
                    console.error(formatError(errorMessage, e));
                    return errorVal;
                });
            }
            detail.resolver(t);
            return t;
        }
        catch (e) {
            console.error(formatError(errorMessage, e));
            return errorVal;
        }
    }
    var vscode;
    function activate(context) {
        vscode = window["getVsCode"]("html");
        let toDispose = context.subscriptions;
        let serverModule = context.asAbsolutePath(path.join('server', 'out', 'htmlServerMain.js'));
        let debugOptions = { execArgv: ['--nolazy', '--inspect=6045'] };
        let serverOptions = {
            run: { module: serverModule, transport: vscode_languageclient_1.TransportKind.ipc },
            debug: { module: serverModule, transport: vscode_languageclient_1.TransportKind.ipc, options: debugOptions }
        };
        let documentSelector = ['html', 'handlebars', 'razor'];
        let embeddedLanguages = { css: true, javascript: true };
        let clientOptions = {
            documentSelector,
            synchronize: {
                configurationSection: ['html', 'css', 'javascript'],
            },
            initializationOptions: {
                embeddedLanguages
            },
            vscode: vscode
        };
        let workspaceFolders;
        var languageModes;
        let clientSnippetSupport;
        let scopedSettingsSupport;
        var globalSettings = {};
        var documents = new Map();
        vscode.workspace.textDocuments.forEach((doc) => {
            documents.set(doc.uri.toString(), doc);
        });
        function getDocumentSettings(textDocument, needsDocumentSettings) {
            if (scopedSettingsSupport && needsDocumentSettings()) {
                let promise = documents.get(textDocument.uri.toString());
                if (!promise) {
                    let scopeUri = textDocument.uri;
                    let configRequestParam = { items: [{ scopeUri, section: 'css' }, { scopeUri, section: 'html' }, { scopeUri, section: 'javascript' }] };
                    var uuid = vsclient.UUID.generateUuid();
                    promise = new Promise((resolve, reject) => {
                        var ev = new CustomEvent("html_request_" + vsproto.ConfigurationRequest.type.method, {
                            detail: {
                                uuid: uuid,
                                params: configRequestParam,
                                resolver: resolve,
                                rejecter: reject
                            }
                        });
                        window.dispatchEvent(ev);
                    }).then(s => ({ css: s[0], html: s[1], javascript: s[2] }));
                    documents.set(textDocument.uri.toString(), promise);
                }
                return promise;
            }
            return Promise.resolve(void 0);
        }
        window.addEventListener("html_request_initialize", (ev) => {
            var detail = ev.detail.params;
            let initializationOptions = detail.initializationOptions;
            workspaceFolders = detail.workspaceFolders;
            if (!Array.isArray(workspaceFolders)) {
                workspaceFolders = [];
                if (detail.rootPath) {
                    workspaceFolders.push({ name: '', uri: vscode.uri.file(detail.rootPath).toString() });
                }
            }
            languageModes = languageModes_1.getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true });
            vscode.workspace.onDidCloseTextDocument((doc) => {
                languageModes.onDocumentRemoved(doc);
                documents.delete(doc.uri.toString());
            });
            vscode.workspace.onDidOpenTextDocument((doc) => {
                documents.set(doc.uri.toString(), doc);
            });
            function hasClientCapability(...keys) {
                let c = detail.capabilities;
                for (let i = 0; c && i < keys.length; i++) {
                    c = c[keys[i]];
                }
                return !!c;
            }
            clientSnippetSupport = hasClientCapability('textDocument', 'completion', 'completionItem', 'snippetSupport');
            scopedSettingsSupport = hasClientCapability('workspace', 'configuration');
            let capabilities = {
                textDocumentSync: 1,
                completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined,
                hoverProvider: true,
                documentHighlightProvider: true,
                documentRangeFormattingProvider: false,
                documentLinkProvider: { resolveProvider: false },
                documentSymbolProvider: true,
                definitionProvider: true,
                signatureHelpProvider: { triggerCharacters: ['('] },
                referencesProvider: true,
                colorProvider: true
            };
            var cb = ev.detail["requests"].get("html_request_initialize_" + ev.detail.uuid);
            cb && cb({ capabilities });
        });
        let client = new vscode_languageclient_1.LanguageClient('html', 'HTML Language Server', serverOptions, clientOptions);
        client.registerProposedFeatures();
        client.start();
        client.onReady().then(() => {
            let tagRequestor = (document, position) => {
                let param = client.code2ProtocolConverter.asTextDocumentPositionParams(document, position);
                return client.sendRequest(TagCloseRequest.type, param);
            };
            var disposable = tagClosing_1.activateTagClosing(tagRequestor, { html: true, handlebars: true, razor: true }, 'html.autoClosingTags', vscode);
            toDispose.push(disposable);
        });
        vscode.languages.setLanguageConfiguration('html', {
            indentationRules: {
                increaseIndentPattern: /<(?!\?|(?:area|base|br|col|frame|hr|html|img|input|link|meta|param)\b|[^>]*\/>)([-_\.A-Za-z0-9]+)(?=\s|>)\b[^>]*>(?!.*<\/\1>)|<!--(?!.*-->)|\{[^}"']*$/,
                decreaseIndentPattern: /^\s*(<\/(?!html)[-_\.A-Za-z0-9]+\b[^>]*>|-->|\})/
            },
            wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,
            onEnterRules: [
                {
                    beforeText: new RegExp(`<(?!(?:${htmlEmptyTagsShared_1.EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
                    afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>/i,
                    action: { indentAction: vscode.IndentAction.IndentOutdent }
                },
                {
                    beforeText: new RegExp(`<(?!(?:${htmlEmptyTagsShared_1.EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
                    action: { indentAction: vscode.IndentAction.Indent }
                }
            ],
        });
        vscode.languages.setLanguageConfiguration('handlebars', {
            wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,
            onEnterRules: [
                {
                    beforeText: new RegExp(`<(?!(?:${htmlEmptyTagsShared_1.EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
                    afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>/i,
                    action: { indentAction: vscode.IndentAction.IndentOutdent }
                },
                {
                    beforeText: new RegExp(`<(?!(?:${htmlEmptyTagsShared_1.EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
                    action: { indentAction: vscode.IndentAction.Indent }
                }
            ],
        });
        vscode.languages.setLanguageConfiguration('razor', {
            wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,
            onEnterRules: [
                {
                    beforeText: new RegExp(`<(?!(?:${htmlEmptyTagsShared_1.EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
                    afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>/i,
                    action: { indentAction: vscode.IndentAction.IndentOutdent }
                },
                {
                    beforeText: new RegExp(`<(?!(?:${htmlEmptyTagsShared_1.EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
                    action: { indentAction: vscode.IndentAction.Indent }
                }
            ],
        });
        const regionCompletionRegExpr = /^(\s*)(<(!(-(-\s*(#\w*)?)?)?)?)?$/;
        vscode.languages.registerCompletionItemProvider(documentSelector, {
            provideCompletionItems(doc, pos) {
                let lineUntilPos = doc.getText(new vscode.Range(new vscode.Position(pos.line, 0), pos));
                let match = lineUntilPos.match(regionCompletionRegExpr);
                if (match) {
                    let range = new vscode.Range(new vscode.Position(pos.line, match[1].length), pos);
                    let beginProposal = new vscode.CompletionItem('#region', vscode.CompletionItemKind.Snippet);
                    beginProposal.range = range;
                    beginProposal.insertText = new vscode.SnippetString('<!-- #region $1-->');
                    beginProposal.documentation = 'Folding Region Start';
                    beginProposal.filterText = match[2];
                    beginProposal.sortText = 'za';
                    let endProposal = new vscode.CompletionItem('#endregion', vscode.CompletionItemKind.Snippet);
                    endProposal.range = range;
                    endProposal.insertText = new vscode.SnippetString('<!-- #endregion -->');
                    endProposal.documentation = 'Folding Region End';
                    endProposal.filterText = match[2];
                    endProposal.sortText = 'zb';
                    return [beginProposal, endProposal];
                }
                return null;
            }
        });
        window.addEventListener("html_request_" + vscode_languageserver_protocol_1.CompletionRequest.type.method, (ev) => {
            var textDocumentPosition = ev.detail.params[0];
            runSafe(() => __awaiter(this, void 0, void 0, function* () {
                let document = documents.get(textDocumentPosition.textDocument.uri);
                let mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);
                if (mode && mode.doComplete) {
                    let doComplete = mode.doComplete;
                    let settings = yield getDocumentSettings(document, () => doComplete.length > 2);
                    return doComplete(document, textDocumentPosition.position, settings);
                }
                return { isIncomplete: true, items: [] };
            }), null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("html_request_" + vscode_languageserver_protocol_1.CompletionResolveRequest.type.method, (ev) => {
            var item = ev.detail.params[0];
            runSafe(() => {
                let data = item.data;
                if (data && data.languageId && data.uri) {
                    let mode = languageModes.getMode(data.languageId);
                    let document = documents.get(data.uri);
                    if (mode && mode.doResolve && document) {
                        return mode.doResolve(document, item);
                    }
                }
                return item;
            }, null, `Error while resolving completion proposal`, ev.detail);
        });
        window.addEventListener("html_request_" + vscode_languageserver_protocol_1.HoverRequest.type.method, (ev) => {
            var textDocumentPosition = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(textDocumentPosition.textDocument.uri);
                let mode = languageModes.getModeAtPosition(document, textDocumentPosition.position);
                if (mode && mode.doHover) {
                    return mode.doHover(document, textDocumentPosition.position);
                }
                return null;
            }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("html_request_" + vscode_languageserver_protocol_1.DocumentHighlightRequest.type.method, (ev) => {
            var documentHighlightParams = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(documentHighlightParams.textDocument.uri);
                let mode = languageModes.getModeAtPosition(document, documentHighlightParams.position);
                if (mode && mode.findDocumentHighlight) {
                    return mode.findDocumentHighlight(document, documentHighlightParams.position);
                }
                return [];
            }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("html_request_" + vscode_languageserver_protocol_1.DefinitionRequest.type.method, (ev) => {
            var definitionParams = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(definitionParams.textDocument.uri);
                let mode = languageModes.getModeAtPosition(document, definitionParams.position);
                if (mode && mode.findDefinition) {
                    return mode.findDefinition(document, definitionParams.position);
                }
                return [];
            }, null, `Error while computing definitions for ${definitionParams.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("html_request_" + vscode_languageserver_protocol_1.ReferencesRequest.type.method, (ev) => {
            var referenceParams = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(referenceParams.textDocument.uri);
                let mode = languageModes.getModeAtPosition(document, referenceParams.position);
                if (mode && mode.findReferences) {
                    return mode.findReferences(document, referenceParams.position);
                }
                return [];
            }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("html_request_" + vscode_languageserver_protocol_1.SignatureHelpRequest.type.method, (ev) => {
            var signatureHelpParms = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(signatureHelpParms.textDocument.uri);
                let mode = languageModes.getModeAtPosition(document, signatureHelpParms.position);
                if (mode && mode.doSignatureHelp) {
                    return mode.doSignatureHelp(document, signatureHelpParms.position);
                }
                return null;
            }, null, `Error while computing signature help for ${signatureHelpParms.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("html_request_" + vscode_languageserver_protocol_1.DocumentRangeFormattingRequest.type.method, (ev) => __awaiter(this, void 0, void 0, function* () {
            var formatParams = ev.detail.params[0];
            runSafe(() => __awaiter(this, void 0, void 0, function* () {
                let document = documents.get(formatParams.textDocument.uri);
                let settings = yield getDocumentSettings(document, () => true);
                if (!settings) {
                    settings = globalSettings;
                }
                let unformattedTags = settings && settings.html && settings.html.format && settings.html.format.unformatted || '';
                let enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) };
                return formatting_1.format(languageModes, document, formatParams.range, formatParams.options, settings, enabledModes);
            }), [], `Error while formatting range for ${formatParams.textDocument.uri}`, ev.detail);
        }));
        window.addEventListener("html_request_" + vscode_languageserver_protocol_1.DocumentLinkRequest.type.method, (ev) => {
            var documentLinkParam = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(documentLinkParam.textDocument.uri);
                let links = [];
                if (document) {
                    let documentContext = documentContext_1.getDocumentContext(document.uri, workspaceFolders);
                    languageModes.getAllModesInDocument(document).forEach((m) => {
                        if (m.findDocumentLinks) {
                            arrays_1.pushAll(links, m.findDocumentLinks(document, documentContext));
                        }
                    });
                }
                return links;
            }, new Array(), `Error while document links for ${documentLinkParam.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("html_request_" + vscode_languageserver_protocol_1.DocumentSymbolRequest.type.method, (ev) => {
            var documentSymbolParms = ev.detail.params[0];
            return runSafe(() => {
                let document = documents.get(documentSymbolParms.textDocument.uri);
                let symbols = [];
                languageModes.getAllModesInDocument(document).forEach((m) => {
                    if (m.findDocumentSymbols) {
                        arrays_1.pushAll(symbols, m.findDocumentSymbols(document));
                    }
                });
                return symbols;
            }, new Array(), `Error while computing document symbols for ${documentSymbolParms.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("html_request_" + vsproto.DocumentColorRequest.type.method, (ev) => {
            var params = ev.detail.params[0];
            runSafe(() => {
                let infos = [];
                let document = documents.get(params.textDocument.uri);
                if (document) {
                    languageModes.getAllModesInDocument(document).forEach((m) => {
                        if (m.findDocumentColors) {
                            arrays_1.pushAll(infos, m.findDocumentColors(document));
                        }
                    });
                }
                return infos;
            }, new Array(), `Error while computing document colors for ${params.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("html_request_" + vsproto.ColorPresentationRequest.type.method, (ev) => {
            var params = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(params.textDocument.uri);
                if (document) {
                    let mode = languageModes.getModeAtPosition(document, params.range.start);
                    if (mode && mode.getColorPresentations) {
                        return mode.getColorPresentations(document, params.color, params.range);
                    }
                }
                return [];
            }, [], `Error while computing color presentations for ${params.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("html_request_" + TagCloseRequest.type.method, (ev) => {
            var params = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(params.textDocument.uri);
                if (document) {
                    let pos = params.position;
                    if (pos.character > 0) {
                        let mode = languageModes.getModeAtPosition(document, vscode_languageserver_types_1.Position.create(pos.line, pos.character - 1));
                        if (mode && mode.doAutoClose) {
                            return mode.doAutoClose(document, pos);
                        }
                    }
                }
                return null;
            }, null, `Error while computing tag close actions for ${params.textDocument.uri}`, ev.detail);
        });
    }
    exports.activate = activate;
});
